/**
  ******************************************************************************
  * @file    bsp_uart_ex.c
  * @author  Iron
  * @date    2021-01-31
  * @version v1.0
  * @brief   bsp_uart_ex c file
  */

/* Private includes ----------------------------------------------------------*/
#include "bsp_uart_ex.h"

/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
#define BSP_UART_TIMEOUT            (-2)
#define BSP_UART_ERR                (-1)
#define BSP_UART_OK                 (0)

/* uart event bits config */
#define BSP_UART_TXC_EVNET          (1 << 0)
#define BSP_UART_RXC_EVNET          (1 << 1)
#define BSP_UART_ERR_EVNET          (1 << 2)

#define BSP_UART1_EVNET_BIT_BASE    (0)
#define BSP_UART2_EVNET_BIT_BASE    (4)

#define BSP_UART1_EVT(event_bits)   ((event_bits) << BSP_UART1_EVNET_BIT_BASE)
#define BSP_UART2_EVT(event_bits)   ((event_bits) << BSP_UART2_EVNET_BIT_BASE)


/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
static TX_EVENT_FLAGS_GROUP uart_event_handle;

/* Private function prototypes -----------------------------------------------*/
static int32_t bsp_usart_set_event(bsp_uart_id_t uart_id, uint32_t event);
static int32_t bsp_usart_wait_event(bsp_uart_id_t uart_id, uint32_t event, uint32_t ticks);

/**
  * @brief Tx Transfer completed callback.
  * @param huart UART handle.
  * @retval None
  */
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
    bsp_usart_set_event(bsp_get_uart_id(huart), BSP_UART_TXC_EVNET);
}

/**
  * @brief  Rx Transfer completed callback.
  * @param  huart UART handle.
  * @retval None
  */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
    bsp_usart_set_event(bsp_get_uart_id(huart), BSP_UART_RXC_EVNET);
}

/**
  * @brief  Reception Event Callback (Rx event notification called after use of advanced reception service).
  * @param  huart UART handle
  * @param  Size  Number of data available in application reception buffer (indicates a position in
  *               reception buffer until which, data are available)
  * @retval None
  */
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{
    /* Prevent unused argument(s) compilation warning */
    UNUSED(huart);
    UNUSED(Size);

    /* NOTE : This function should not be modified, when the callback is needed,
              the HAL_UARTEx_RxEventCallback can be implemented in the user file.
     */
}

/**
  * @brief  UART error callback.
  * @param  huart UART handle.
  * @retval None
  */
void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)
{
    uint32_t errcode = huart->ErrorCode;

    /*!< No error                */
    if (errcode == HAL_UART_ERROR_NONE)
        return;

    /*!< Parity error            */
    if (errcode & HAL_UART_ERROR_PE)
    {
//        bsp_usart_set_event(bsp_get_uart_id(huart), BSP_UART_ERR_EVNET);
    }

    /*!< Noise error             */
    if (errcode & HAL_UART_ERROR_NE)
    {
//        bsp_usart_set_event(bsp_get_uart_id(huart), BSP_UART_ERR_EVNET);
    }

    /*!< Frame error             */
    if (errcode & HAL_UART_ERROR_FE)
    {
//        bsp_usart_set_event(bsp_get_uart_id(huart), BSP_UART_ERR_EVNET);
    }

    /*!< Overrun error           */
    if (errcode & HAL_UART_ERROR_ORE)
    {
        bsp_usart_set_event(bsp_get_uart_id(huart), BSP_UART_ERR_EVNET);
    }

    /*!< DMA transfer error      */
    if (errcode & HAL_UART_ERROR_DMA)
    {
         bsp_usart_set_event(bsp_get_uart_id(huart), BSP_UART_ERR_EVNET);
    }

    /*!< Receiver Timeout error  */
    if (errcode & HAL_UART_ERROR_RTO)
    {
        bsp_usart_set_event(bsp_get_uart_id(huart), BSP_UART_RXC_EVNET);
    }
}

/*----------------------------------------------------------------------------*/
int32_t bsp_uart_poll_tx(bsp_uart_id_t uart_id, uint8_t *buf, size_t buflen, uint32_t timeout)
{
    UART_HandleTypeDef *huart = bsp_get_uart_handle(uart_id);

    if (huart == NULL)
        return 0;

    HAL_UART_Transmit(huart, buf, buflen, timeout);

    return 0;
}

/*----------------------------------------------------------------------------*/
int32_t bsp_uart_poll_rx(bsp_uart_id_t uart_id, uint8_t *buf, size_t buflen, uint32_t timeout)
{
    UART_HandleTypeDef *huart = bsp_get_uart_handle(uart_id);
    uint16_t rxlen = 0;

    if (huart == NULL)
        return 0;
    
    if(timeout == TX_WAIT_FOREVER)
    {
        HAL_UART_Receive(huart, buf, buflen, HAL_MAX_DELAY);
    }
    else
    {
        HAL_UARTEx_ReceiveToIdle(huart, buf, buflen, &rxlen, timeout); // timeout: ms
    }

    return rxlen;
}

/*----------------------------------------------------------------------------*/
int32_t bsp_uart_it_tx(bsp_uart_id_t uart_id, uint8_t *buf, size_t buflen, uint32_t timeout)
{
    UART_HandleTypeDef *huart = bsp_get_uart_handle(uart_id);
    UINT status;

    if (huart == NULL)
        return BSP_UART_ERR;

    // clear rxc bits
    bsp_usart_wait_event(uart_id, (BSP_UART_TXC_EVNET | BSP_UART_ERR_EVNET), TX_NO_WAIT);

    HAL_UART_Transmit_IT(huart, buf, buflen);

    status = bsp_usart_wait_event(uart_id, (BSP_UART_TXC_EVNET | BSP_UART_ERR_EVNET), TX_MS_TO_TICKS(timeout));

    if (status != BSP_UART_OK)
        return status;

    return buflen;
}

/*----------------------------------------------------------------------------*/
int32_t bsp_uart_it_rx(bsp_uart_id_t uart_id, uint8_t *buf, size_t buflen, uint32_t timeout)
{
    UART_HandleTypeDef *huart = bsp_get_uart_handle(uart_id);
    UINT status;
    uint16_t rxlen = 0;

    if (huart == NULL)
        return BSP_UART_ERR;

    // clear rxc bits
    bsp_usart_wait_event(uart_id, (BSP_UART_RXC_EVNET | BSP_UART_ERR_EVNET), TX_NO_WAIT);

    if(timeout == TX_WAIT_FOREVER)
    {
        HAL_UART_Receive_IT(huart, buf, buflen);
    }
    else
    {
        HAL_UARTEx_ReceiveToIdle_IT(huart, buf, buflen); // timeout: ms
    }

    status = bsp_usart_wait_event(uart_id, (BSP_UART_RXC_EVNET | BSP_UART_ERR_EVNET), TX_MS_TO_TICKS(timeout));

    if (status != BSP_UART_OK)
        return status;

    rxlen = huart->RxXferCount;

    return rxlen;
}

/*----------------------------------------------------------------------------*/
int32_t bsp_uart_dma_tx(bsp_uart_id_t uart_id, uint8_t *buf, size_t buflen, uint32_t timeout)
{
    UART_HandleTypeDef *huart = bsp_get_uart_handle(uart_id);
    UINT status;

    if (huart == NULL)
        return BSP_UART_ERR;

    // clear rxc bits
    bsp_usart_wait_event(uart_id, (BSP_UART_TXC_EVNET | BSP_UART_ERR_EVNET), TX_NO_WAIT);

    HAL_UART_Transmit_DMA(huart, buf, buflen);

    status = bsp_usart_wait_event(uart_id, (BSP_UART_TXC_EVNET | BSP_UART_ERR_EVNET), TX_MS_TO_TICKS(timeout));

    if (status != BSP_UART_OK)
        return status;

    return buflen;
}

/*----------------------------------------------------------------------------*/
int32_t bsp_uart_dma_rx(bsp_uart_id_t uart_id, uint8_t *buf, size_t buflen, uint32_t timeout)
{
    UART_HandleTypeDef *huart = bsp_get_uart_handle(uart_id);
    UINT status;
    uint16_t rxlen = 0;

    if (huart == NULL)
        return BSP_UART_ERR;

    // clear rxc bits
    bsp_usart_wait_event(uart_id, (BSP_UART_RXC_EVNET | BSP_UART_ERR_EVNET), TX_NO_WAIT);

    if(timeout == TX_WAIT_FOREVER)
    {
        HAL_UART_Receive_DMA(huart, buf, buflen);
    }
    else
    {
        HAL_UARTEx_ReceiveToIdle_DMA(huart, buf, buflen); // timeout: ms
    }

    status = bsp_usart_wait_event(uart_id, (BSP_UART_RXC_EVNET | BSP_UART_ERR_EVNET), TX_MS_TO_TICKS(timeout));

    if (status != BSP_UART_OK)
        return status;

    rxlen = huart->RxXferCount;

    return rxlen;
}

/*----------------------------------------------------------------------------*/
static int32_t bsp_usart_set_event(bsp_uart_id_t uart_id, uint32_t event_bits)
{
    UINT status;
    ULONG flags_to_set;

    if (uart_id == BSP_UART1)
        flags_to_set = BSP_UART1_EVT(event_bits);
    else if (uart_id == BSP_UART2)
        flags_to_set = BSP_UART2_EVT(event_bits);
    else
        return BSP_UART_ERR;

    status = tx_event_flags_set(&uart_event_handle, flags_to_set, TX_OR);

    if (status != TX_SUCCESS)
        return BSP_UART_ERR;

    return BSP_UART_OK;
}

static int32_t bsp_usart_wait_event(bsp_uart_id_t uart_id, uint32_t event_bits, uint32_t ticks)
{
    UINT status;
    ULONG requested_flags, actual_flags;

    if (uart_id == BSP_UART1)
        requested_flags = BSP_UART1_EVT(event_bits);
    else if (uart_id == BSP_UART2)
        requested_flags = BSP_UART2_EVT(event_bits);
    else
        return BSP_UART_ERR;

    status = tx_event_flags_get(&uart_event_handle, requested_flags, TX_OR_CLEAR, &actual_flags, ticks); // TX_NO_WAIT / TX_WAIT_FOREVER

    if (status != TX_SUCCESS)
        return BSP_UART_TIMEOUT;

    return BSP_UART_OK;
}

/*----------------------------------------------------------------------------*/
int32_t bsp_usart_init_ex(void)
{
    UINT status;

    status = tx_event_flags_create(&uart_event_handle, "uart_event");

    if (status != TX_SUCCESS)
    {
        Error_Handler();
    }

    bsp_usart_init();

    return 0;
}


/**
  * @}
  */

/******************* (C)COPYRIGHT 2021 ***** END OF FILE *********************/
